﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.IO;
using AbstraX.Contracts;
using Metaspec;
using Utils;

namespace VisualStudioProvider
{
    public delegate void OnParserErrorHandler(object sender, EventArgs<Exception> args);
    public delegate void OnParserErrorRawHandler(object sender, Exception ex);

    public class VSSolution : IVSSolution
    {
        //internal class SolutionParser 
        //Name: Microsoft.Build.Construction.SolutionParser 
        //Assembly: Microsoft.Build, Version=4.0.0.0 

        static readonly Type s_SolutionParser;
        static readonly PropertyInfo s_SolutionParser_solutionReader;
        static readonly MethodInfo s_SolutionParser_parseSolution;
        static readonly PropertyInfo s_SolutionParser_projects;
        private string solutionFileName;
        private List<VSProject> projects;
        private ISolution iSolution;
        public static event OnParserErrorHandler OnParserError;
        public static event OnParserErrorRawHandler OnParserErrorRaw;

        public IProjectRoot ProjectRoot { get; set; }
        public IArchitectureLayer ArchitectureLayer { get; set; }

        public ISolution ISolution
        {
            get 
            {
                if (iSolution == null)
                {
                    var iSolutionParser = new SolutionParser();

                    iSolution = iSolutionParser.Parse(solutionFileName);
                }

                return iSolution; 
            }
        }

        public IProjectStructure FindLayer(string layerName)
        {
            throw new NotImplementedException();
        }

        public string Name
        {
            get
            {
                return Path.GetFileNameWithoutExtension(solutionFileName);
            }
        }

        public IEnumerable<IVSProject> Projects
        {
            get
            {
                return projects;
            }
        }

        static VSSolution()
        {
            s_SolutionParser = Type.GetType("Microsoft.Build.Construction.SolutionParser, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
            s_SolutionParser_solutionReader = s_SolutionParser.GetProperty("SolutionReader", BindingFlags.NonPublic | BindingFlags.Instance);
            s_SolutionParser_projects = s_SolutionParser.GetProperty("Projects", BindingFlags.NonPublic | BindingFlags.Instance);
            s_SolutionParser_parseSolution = s_SolutionParser.GetMethod("ParseSolution", BindingFlags.NonPublic | BindingFlags.Instance);
        }

        public string SolutionPath
        {
            get
            {
                var file = new FileInfo(solutionFileName);

                return file.DirectoryName;
            }
        }

        public VSSolution(string solutionFileName)
        {
            if (s_SolutionParser == null)
            {
                throw new InvalidOperationException("Can not find type 'Microsoft.Build.Construction.SolutionParser' are you missing a assembly reference to 'Microsoft.Build.dll'?");
            }

            var solutionParser = s_SolutionParser.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).First().Invoke(null);

            using (var streamReader = new StreamReader(solutionFileName))
            {
                s_SolutionParser_solutionReader.SetValue(solutionParser, streamReader, null);
                s_SolutionParser_parseSolution.Invoke(solutionParser, null);
            }

            this.solutionFileName = solutionFileName;

            projects = new List<VSProject>();
            var array = (Array)s_SolutionParser_projects.GetValue(solutionParser, null);

            for (int i = 0; i < array.Length; i++)
            {
                try
                {
                    projects.Add(new VSProject(this, array.GetValue(i)));
                }
                catch (Exception ex)
                {
                    if (OnParserError != null)
                    {
                        OnParserError(array.GetValue(i), new EventArgs<Exception>(ex));
                    }

                    if (OnParserErrorRaw != null)
                    {
                        OnParserErrorRaw(array.GetValue(i), ex);
                    }
                }
            }
        }

        public IEnumerable<IVSProjectItem> EDMXModels
        {
            get { throw new NotImplementedException(); }
        }

        public void Dispose()
        {
        }

        public IEnumerable<IVSProjectItem> Schemas
        {
            get { throw new NotImplementedException(); }
        }
    }
}